#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Task.hpp"
#include "Channel.hpp"
using namespace std;
using work_t = function<void()>;

enum
{
    OK,
    USAGE_ERROR,
    PIPE_ERROR,
    FORK_ERROR
};

class ProcessPool
{
public:
    ProcessPool(int n, work_t work)
        :_processnum(n)
        ,_work(work)
    {}

    int InitProcesspool()
    {
        for (int i = 0; i < _processnum; i++) // 执行这个循环的只能是父进程
        {
            // 1、先创建管道
            int pipefd[2] = {0};
            int n = pipe(pipefd);
            if (n < 0)
                return PIPE_ERROR;
            // 2、创建子进程
            pid_t id = fork();
            if (id < 0)
                return FORK_ERROR;
            // 3、建立通信信道
            if (id == 0)
            {
                //关闭历史fd
                cout << getpid() << ", child-close history fd: ";
                for (auto &c : _channels)
                {
                    cout << c.Wfd() << " ";
                    c.Close();
                }
                cout << " over." << endl;
                // 子进程
                close(pipefd[1]);
                dup2(pipefd[0], 0); // 子进程从标准输入读取
                _work();
                exit(0);
            }
            // 父进程
            close(pipefd[0]);
            _channels.emplace_back(pipefd[1], id);
        }
        return OK;
    }

    void DispatchTask()
    {
        int who = 0;
        int num = 20;
        while (num--)
        {
            // a.选择一个任务
            int task = TM.SelectTask();
            // b.选择一个子进程（channel）
            Channel &cur = _channels[who++];
            who %= _channels.size();

            cout << "####################" << endl;
            cout << "send " << task << " to " << cur.Name() << endl;
            cout << "####################" << endl;

            // c.派发任务
            cur.Send(task);

            sleep(1);
        }
    }

    void CleanProcessPool()
    {
        //virsion1
        // for (auto &c : _channels)
        // {
        //     c.Close();
        // }
        // for (auto &c : _channels)
        // {
        //     pid_t rid = waitpid(c.GetId(), nullptr, 0);
        //     if (rid > 0)
        //     {
        //         cout << "child: " << rid << "wait...success" << endl;
        //     }
        // }

        //virsion2
        // for (int i = _channels.size()-1; i >= 0; i--)
        // {
        //     _channels[i].Close();
        //     pid_t rid = waitpid(_channels[i].GetId(), nullptr, 0);
        //     if (rid > 0)
        //     {
        //         cout << "child: " << rid << "wait...success" << endl;
        //     }
        // }

        //virsion3
        for (auto &c : _channels)
        {
            c.Close();
            pid_t rid = waitpid(c.GetId(), nullptr, 0);
            if (rid > 0)
            {
                cout << "child: " << rid << "wait...success" << endl;
            }
        }
    }

    void DebugPrint()
    {
        for (auto &e : _channels)
        {
            cout << e.Name() << endl;
        }
    }

private:
    vector<Channel> _channels;
    int _processnum;
    work_t _work;
};
